package cn.swing.his.common.utils;

import cn.swing.his.common.constant.JwtConst;
import cn.swing.his.common.exception.HisServiceException;
import cn.swing.his.common.pojo.domain.HisAuthenticationInfo;
import cn.swing.his.common.web.JsonResult;
import cn.swing.his.common.web.ResponseCode;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
public class JwtUtils implements JwtConst {

    /**
     * JWT的过期时间
     */
    @Value("${jwt.expiration}")
    private Long expiration;
    /**
     * 密钥
     */
    @Value("${jwt.secret-key}")
    private String secretKey;


    /**
     * 根据医生信息生成自定义数据
     * @param doctorInfo 医生信息
     * @return 自定义数据
     */
    public String generateClaims(HisAuthenticationInfo doctorInfo){
        Map<String,Object> claims = new HashMap<>();
        String doctorInfoJson = null;
        if (doctorInfo == null) {
            throw new HisServiceException(ResponseCode.INTERNAL_SERVER_ERROR,"无法转换用户信息");
        }
        try {
            // 先将用户信息转换成json字符串再写入
            doctorInfoJson = JSON.toJSONString(doctorInfo);
        }catch (Exception e){
            log.info("将用户信息转换成json字符串出现错误，错误信息：{}",e.getMessage());
        }
        claims.put(KEY_CREATED,new Date());
        claims.put(KEY_USERINFO,doctorInfoJson);
        log.info("自定义数据为{}",claims);
        return generateJwt(claims);
    }

    /**
     * 生成jwt
     * @param claims 自定义数据
     * @return jwt
     */
    private String generateJwt(Map<String,Object> claims){
        // JWT的主要组件部分：Header(头),PayLoad(载荷),Signature(签名)
        String jwt = Jwts.builder()
                // Header:指定算法与当前数据类型
                // 格式为:{"alg":算法,"type":"jwt"}
                .setHeaderParam(Header.CONTENT_TYPE,"HS256")
                .setHeaderParam(Header.TYPE,Header.JWT_TYPE)
                // PayLoad:通常包含Claims(自定义数据)和过期时间
                .setClaims(claims) // 自定义数据
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                // Signature:有算法和密钥这两部分组成
                .signWith(SignatureAlgorithm.HS256,secretKey)
                .compact();
        log.info("生成的jwt为{}",jwt);
        return jwt;
    }

    /**
     * 解析JWT
     * @param jwt
     * @return
     */
    public HisAuthenticationInfo parseJwt(String jwt){
        Claims claims = null;
        String doctorInfoJson = null;
        try {
            claims = getClaimsFromJwt(jwt);
            doctorInfoJson  = (String) claims.get(KEY_USERINFO);
            log.info("获取到doctorDetails中各种属性：{}",doctorInfoJson);
        }catch (NullPointerException e) {
            log.info("解析JWT失败，当前JWT无效，无法解析数据：" + e);
            return null;
        }
            // 拿到载荷之后,解析doctorDetails对象
            try {
                HisAuthenticationInfo doctorInfo =
                        JSON.parseObject(doctorInfoJson, HisAuthenticationInfo.class);
                return doctorInfo;
            } catch (Exception e) {
                log.info("尝试解析失败", e);
                return null;
            }
    }

    /**
     * 从JWT中获取载荷
     * @param jwt
     * @return
     */
    private Claims getClaimsFromJwt(String jwt){
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getBody();
            log.info("当需要解析JWT时，获取载荷");
        }catch (Exception e){
            log.info("JWT格式验证失败：{}",jwt);
        }
        return claims;
    }
}
